package oa.hleast.xswl.service.place.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jodd.util.StringUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import oa.hleast.common.constant.CacheConstant;
import oa.hleast.common.redis.component.template.RedisRepository;
import oa.hleast.common.web.exception.BizException;
import oa.hleast.xswl.common.enums.PlaceInfoUrlEnum;
import oa.hleast.xswl.common.enums.ScenesTypeEnum;
import oa.hleast.xswl.mapper.place.PlaceInfoMapper;
import oa.hleast.xswl.mapper.place.PlaceTypeMapper;
import oa.hleast.xswl.pojo.domain.SysCommonConfig;
import oa.hleast.xswl.pojo.domain.place.*;
import oa.hleast.xswl.pojo.dto.UmsUserDto;
import oa.hleast.xswl.pojo.form.place.CmsPlaceInfoAttrValueForm;
import oa.hleast.xswl.pojo.form.place.CmsPlaceInfoCommentForm;
import oa.hleast.xswl.pojo.form.place.CmsPlaceInfoForm;
import oa.hleast.xswl.pojo.vo.place.CmsPlaceInfoAttrValueVo;
import oa.hleast.xswl.pojo.vo.place.CmsPlaceInfoCommentVo;
import oa.hleast.xswl.pojo.vo.place.CmsPlaceInfoVo;
import oa.hleast.xswl.pojo.vo.place.ShowPlaceInfoVo;
import oa.hleast.xswl.service.ISysCommonConfigService;
import oa.hleast.xswl.service.IUmsUserService;
import oa.hleast.xswl.service.clockin.IClockInService;
import oa.hleast.xswl.service.place.IPlaceInfoAttrValueService;
import oa.hleast.xswl.service.place.IPlaceInfoCommentService;
import oa.hleast.xswl.service.place.IPlaceInfoService;
import oa.hleast.xswl.service.place.IScenesScourService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 302
 * @description
 * @date 2021/3/12
 */

@Slf4j
@Service
@AllArgsConstructor
public class PlaceInfoServiceImpl extends ServiceImpl<PlaceInfoMapper, CmsPlaceInfo> implements IPlaceInfoService {
    private static final String RESEARCH = "research";

    private final RedisRepository redisRepository;
    private final PlaceTypeMapper placeTypeMapper;
    private final IPlaceInfoAttrValueService placeInfoAttrValueService;
    private final PlaceInfoMapper placeInfoMapper;
    private final IPlaceInfoCommentService placeInfoCommentService;
    private final IScenesScourService scenesScourService;
    private final IUmsUserService umsUserService;
    private final ISysCommonConfigService commonConfigService;

    /**
     * ===========  业务接口方法 ========
     */

    @Override
    public List<ShowPlaceInfoVo> findShowPlaceInfoByKey(String configKey) {
        SysCommonConfig commonConfig = commonConfigService.getByConfigKey(configKey);
        Long placeTypeId = new Long(commonConfig.getValue());
        List<ShowPlaceInfoVo> cloudShowInfo = this.findShowPlaceInfo(placeTypeId, null, null);
        log.info("cloudShowInfo = {}",cloudShowInfo);
        return cloudShowInfo;
    }

    @Override
    public boolean commentScenicSpot(CmsPlaceInfoCommentForm form, Long userId) {
        UmsUserDto umsUserDto = umsUserService.getUserDtoById(userId);
        if (ObjectUtils.isEmpty(umsUserDto)) {
            throw new BizException("用户不存在");
        }

        CmsPlaceInfo placeInfoOld = this.getById(form.getPlaceInfoId());
        if (ObjectUtils.isEmpty(placeInfoOld)) {
            throw new BizException("场所不存在");
        }

        // 1, 保存对场所的评论
        CmsPlaceInfoComment placeInfoComment = new CmsPlaceInfoComment();
        BeanUtil.copyProperties(form, placeInfoComment);
        //现在不需要多层级的评论回复，评论层级先为 1
        placeInfoComment.setLeve(1);
        placeInfoComment.setCommentUserId(umsUserDto.getId());
        placeInfoComment.setCommentUserName(umsUserDto.getNickname());
        placeInfoComment.setCommentUserAvatar(umsUserDto.getAvatar());
        boolean commentSave = placeInfoCommentService.save(placeInfoComment);

        // 2, 更新该场所的评分数据
        boolean update = scenesScourService.updateByScenesId(placeInfoComment.getPlaceInfoId(), placeInfoComment.getStartRating(), ScenesTypeEnum.PLACE.name());

        log.info("commentSave = {}, update = {}", commentSave, update);
        return commentSave && update;
    }

    @Override
    public List<CmsPlaceInfoCommentVo> findCommentList(Long placeInfoId, Long userId) {
        LambdaQueryWrapper<CmsPlaceInfoComment> queryWrapper = new LambdaQueryWrapper<CmsPlaceInfoComment>()
                .eq(CmsPlaceInfoComment::getPlaceInfoId, placeInfoId)
                .orderByDesc(CmsPlaceInfoComment::getGmtModified)
                .orderByDesc(CmsPlaceInfoComment::getGmtCreate);

        List<CmsPlaceInfoComment> list = placeInfoCommentService.list(queryWrapper);

        boolean isNotEmptyUser = ObjectUtils.isNotEmpty(userId);
        log.info("userId = {} ,isNotEmptyUser = {}", userId, isNotEmptyUser);
        List<CmsPlaceInfoCommentVo> voList = list.stream().map(
                vo -> {
                    CmsPlaceInfoCommentVo umsBlogCommentVo = conversionToCommentVo(vo);
                    if (isNotEmptyUser) {
                        umsBlogCommentVo.setComment(this.isComment(vo.getId(), vo.getCommentUserId()));
                    }
                    return umsBlogCommentVo;
                }
        ).collect(Collectors.toList());
        log.info("voList = {}", voList);
        return voList;
    }

    @Override
    public boolean cloneComment(Long commentId, Long userId) {

        CmsPlaceInfoComment placeInfoComment = placeInfoCommentService.getById(commentId);
        if (ObjectUtils.isEmpty(placeInfoComment)) {
            throw new BizException("该场所不存在");
        }

        // 判断是否是自己的评论
        boolean comment = isComment(commentId, userId);
        if (!comment) {
            throw new BizException("您没有权限撤销该评论");
        }
        placeInfoComment.setDisplay(false);

        return placeInfoCommentService.updateById(placeInfoComment);
    }


    @Override
    public List<CmsPlaceInfo> search(Integer page, Integer limit, String searchKey) {
        LambdaQueryWrapper<CmsPlaceInfo> queryWrapper = new LambdaQueryWrapper<CmsPlaceInfo>()
                .like(CmsPlaceInfo::getKeyword, searchKey)
                .orderByAsc(CmsPlaceInfo::getSort)
                .orderByDesc(CmsPlaceInfo::getGmtModified)
                .orderByDesc(CmsPlaceInfo::getGmtCreate);


        Page<CmsPlaceInfo> cmsPlaceInfoPage = this.page(new Page<>(page, limit), queryWrapper);

        log.info("cmsPlaceInfoPage ={}", cmsPlaceInfoPage);
        return cmsPlaceInfoPage.getRecords();
    }

    /**
     * 递归获取所有子分类Id
     *
     * @param parentId
     * @param cmsPlaceTypeList
     * @return
     */
    public static List<Long> recursionForChildrenId(Long parentId, List<CmsPlaceType> cmsPlaceTypeList) {
        List<Long> list = new ArrayList<>();
        Optional.ofNullable(cmsPlaceTypeList).orElse(new ArrayList<>())
                .stream()
                .filter(cmsPlaceType -> cmsPlaceType.getParentsTypeId().equals(parentId))
                .forEach(cmsPlaceType -> {
                    list.add(cmsPlaceType.getId());
                    List<Long> children = recursionForChildrenId(cmsPlaceType.getId(), cmsPlaceTypeList);
                    list.addAll(children);
                });
        return list;
    }


    @Override
    public List<ShowPlaceInfoVo> findShowPlaceInfo(Long placeTypeId, String name, Integer hasTicket) {


        List<CmsPlaceType> cmsPlaceTypeList = placeTypeMapper.selectList(null);
        // 递归获取所有子类id
        List<Long> allChildrenId = recursionForChildrenId(placeTypeId, cmsPlaceTypeList);
        allChildrenId.add(placeTypeId);

        LambdaQueryWrapper<CmsPlaceInfo> queryWrapper = new LambdaQueryWrapper<CmsPlaceInfo>()
                .eq(CmsPlaceInfo::getDisplay, 1)
                .in(CmsPlaceInfo::getPlaceTypeId, allChildrenId)
                .orderByAsc(CmsPlaceInfo::getSort)
                .orderByDesc(CmsPlaceInfo::getGmtModified)
                .orderByDesc(CmsPlaceInfo::getGmtCreate);
        if (StringUtil.isNotBlank(name)) {
            queryWrapper.like(CmsPlaceInfo::getPlaceName, name);
        }

        List<CmsPlaceInfo> list = this.list(queryWrapper);

        List<ShowPlaceInfoVo> showPlaceInfoList = list.stream().map(cmsPlaceInfo -> {
            ShowPlaceInfoVo showPlaceInfoVo = new ShowPlaceInfoVo();
            BeanUtil.copyProperties(cmsPlaceInfo, showPlaceInfoVo);

            //获取附加属性
            List<CmsPlaceInfoAttrValueVo> valueVoList = getAttrValue(cmsPlaceInfo.getId());
            showPlaceInfoVo.setAttrValueVoList(valueVoList);

            return showPlaceInfoVo;
        }).collect(Collectors.toList());


        log.info("从数据库中获取地图场所列表信息 , showPlaceList => {}", showPlaceInfoList);
        return showPlaceInfoList;
    }

    private final IClockInService clockInService;

    @Override
    @Cacheable(value = CacheConstant.PLACE_INFO_DETAILS_KEY, key = "#id")
    public CmsPlaceInfoVo getShowDetails(Long id, Long userId) {
        CmsPlaceInfoVo cmsPlaceInfoVo = conversionToVo(this.getById(id));

        //是否收藏 是否预约过
        boolean isFavorites = false;
        cmsPlaceInfoVo.setFavorites(isFavorites);

        cmsPlaceInfoVo.setClockIn(clockInService.isClockIn(id,userId,RESEARCH));

        // 评分
        String placeScore = getPlaceScore(id);
        cmsPlaceInfoVo.setScore("0".equals(placeScore) ? "5.0" : placeScore);

        //获取附加属性
        List<CmsPlaceInfoAttrValueVo> valueVoList = getAttrValue(id);
        cmsPlaceInfoVo.setAttrValueVoList(valueVoList);

        log.info("从数据库中获取场所详细信息 id ={} ,cmsPlaceInfoVo = {}", id, cmsPlaceInfoVo);
        return cmsPlaceInfoVo;
    }

    private String getPlaceScore(Long placeInfoId) {
        CmsScenesScour scenesScour = scenesScourService.getByScenesId(placeInfoId, ScenesTypeEnum.PLACE.name());
        if (ObjectUtils.isEmpty(scenesScour)) {
            return "0";
        }

        Integer totalScourNum = scenesScour.getTotalScourNum();
        Integer joinNum = scenesScour.getJoinNum();
        if (joinNum == 0) {
            return "0";
        }

        String score = new BigDecimal(totalScourNum).divide(new BigDecimal(joinNum), 2).setScale(1, RoundingMode.HALF_UP).toString();
        log.info("score = {}", score);
        return score;
    }

    @Transactional
    @Override
    public boolean copy(List<String> asList, Long newPlaceTypeId) {
        asList.forEach(id -> {
            CmsPlaceInfo cmsPlaceInfo = this.getById(id);
            CmsPlaceInfoForm cmsPlaceInfoForm = new CmsPlaceInfoForm();
            BeanUtil.copyProperties(cmsPlaceInfo, cmsPlaceInfoForm);
            cmsPlaceInfoForm.setPlaceTypeId(newPlaceTypeId);
            this.add(cmsPlaceInfoForm);
        });

        return true;
    }

    /**
     * =========  基础功能 CRUD =========
     */
    @Transactional(readOnly = false)
    @Override
    @CacheEvict(value = CacheConstant.PLACE_INFO_LIST_KEY, allEntries = true)
    public boolean add(CmsPlaceInfoForm cmsPlaceInfoForm) {
        log.info("cmsPlaceInfoForm = {}", cmsPlaceInfoForm);

        //校验URL属性
        if (ObjectUtils.isEmpty(PlaceInfoUrlEnum.getEnum(cmsPlaceInfoForm.getPlaceInfoUrlType()))) {
            throw new BizException("跳转路径参数错误");
        }

        CmsPlaceInfo cmsPlaceInfo = conversionToPojo(cmsPlaceInfoForm);

        // 1,  保存地图信息数据
        boolean save = this.save(cmsPlaceInfo);

        // 2， 保存附属属性
        List<CmsPlaceInfoAttrValueForm> attrFormList = cmsPlaceInfoForm.getAttrForm();
        boolean attrSave = true;
        if (CollectionUtils.isNotEmpty(attrFormList) && save) {
            log.info("保存附属属性，attrFormList ={}", attrFormList);
            List<CmsPlaceInfoAttrValue> list = addPlaceInfoAttrValue(cmsPlaceInfo.getId(), attrFormList);
            attrSave = placeInfoAttrValueService.saveBatch(list);
            log.info("保存附属属性，list => {}", list);
        }

        // 3，创建评分数据
        boolean scourSave = scenesScourService.save(CmsScenesScour.builder()
                .scenesId(cmsPlaceInfo.getId())
                .type(ScenesTypeEnum.PLACE.name())
                .build());

        return save && attrSave && scourSave;
    }

    @Override
    public Page<CmsPlaceInfoVo> pageList(Integer page, Integer limit, Integer display, String placeName, String placeTypeId) {

        LambdaQueryWrapper<CmsPlaceInfo> queryWrapper = new LambdaQueryWrapper<CmsPlaceInfo>()
                .eq(CmsPlaceInfo::getDeleted, 0)
                .orderByAsc(CmsPlaceInfo::getSort)
                .orderByDesc(CmsPlaceInfo::getGmtModified)
                .orderByDesc(CmsPlaceInfo::getGmtCreate);

        //各类查询条件
        if (ObjectUtils.isNotEmpty(display)) {
            queryWrapper.eq(CmsPlaceInfo::getDisplay, display);
        }
        if (ObjectUtils.isNotEmpty(placeName)) {
            queryWrapper.like(CmsPlaceInfo::getPlaceName, placeName);
        }
        if (ObjectUtils.isNotEmpty(placeTypeId)) {
            queryWrapper.eq(CmsPlaceInfo::getPlaceTypeId, placeTypeId);
        }

        Page<CmsPlaceInfo> cmsPlaceInfoPage = this.page(new Page<>(page, limit), queryWrapper);

        if (cmsPlaceInfoPage.getTotal() == 0) {
            return new Page<>(page, limit);
        }

        log.info("records = {}", cmsPlaceInfoPage.getRecords());
        List<CmsPlaceInfoVo> cmsPlaceInfoList = cmsPlaceInfoPage.getRecords().stream().map(this::conversionToVo).collect(Collectors.toList());
        log.info("cmsPlaceInfoList = {}", cmsPlaceInfoList);

        Page<CmsPlaceInfoVo> cmsPlaceInfoVoPage = new Page<>(page, limit, cmsPlaceInfoPage.getTotal());
        cmsPlaceInfoVoPage.setRecords(cmsPlaceInfoList);


        return cmsPlaceInfoVoPage;
    }

    @Transactional(readOnly = false)
    @Override
    @Caching(evict = {
            @CacheEvict(value = CacheConstant.PLACE_INFO_LIST_KEY, allEntries = true),
            @CacheEvict(value = CacheConstant.PLACE_INFO_DETAILS_KEY, allEntries = true),
            @CacheEvict(value = CacheConstant.HOME_PAGE_RECOMMEND_PLACE, key = "#placeInfoForm.id")
    })
    public boolean update(CmsPlaceInfoForm placeInfoForm) {

        //校验ID
        CmsPlaceInfo cmsPlaceInfoOld = this.getById(placeInfoForm.getId());
        if (ObjectUtils.isEmpty(cmsPlaceInfoOld)) {
            throw new BizException("没有找到该数据！");
        }
        //校验URL属性
        if (ObjectUtils.isEmpty(PlaceInfoUrlEnum.getEnum(placeInfoForm.getPlaceInfoUrlType()))) {
            throw new BizException("跳转路径参数错误");
        }

        CmsPlaceInfo cmsPlaceInfo = new CmsPlaceInfo();
        BeanUtil.copyProperties(placeInfoForm, cmsPlaceInfo);
        cmsPlaceInfo.setId(cmsPlaceInfoOld.getId());

        boolean saveStatus = this.updateById(cmsPlaceInfo);

        List<CmsPlaceInfoAttrValueForm> attrFormList = placeInfoForm.getAttrForm();
        boolean attrSave = true;
        if (CollectionUtils.isNotEmpty(attrFormList) && saveStatus) {
            // 先清空附加属性
            placeInfoAttrValueService.remove(new LambdaQueryWrapper<CmsPlaceInfoAttrValue>().eq(CmsPlaceInfoAttrValue::getPlaceInfoId, placeInfoForm.getId()));

            List<CmsPlaceInfoAttrValue> list = addPlaceInfoAttrValue(placeInfoForm.getId(), attrFormList);
            attrSave = placeInfoAttrValueService.saveBatch(list);
            log.info("保存附属属性，list => {}", list);
        }

        return saveStatus && attrSave;
    }

    private List<CmsPlaceInfoAttrValue> addPlaceInfoAttrValue(Long placeInfoId, List<CmsPlaceInfoAttrValueForm> attrFormList) {
        List<CmsPlaceInfoAttrValue> list = attrFormList.stream().map(form -> {
            CmsPlaceInfoAttrValue attributes = new CmsPlaceInfoAttrValue();
            BeanUtil.copyProperties(form, attributes);
            attributes.setPlaceInfoId(placeInfoId);
            return attributes;
        }).collect(Collectors.toList());
        return list;
    }

    @Transactional(readOnly = false)
    @Override
    @Caching(evict = {
            @CacheEvict(value = CacheConstant.PLACE_INFO_LIST_KEY, allEntries = true),
            @CacheEvict(value = CacheConstant.PLACE_INFO_DETAILS_KEY, allEntries = true),
            @CacheEvict(value = CacheConstant.HOME_PAGE_RECOMMEND_PLACE, allEntries = true)
    })
    public boolean deleteByIds(List<String> asList) {
        if (CollectionUtils.isFull(asList)) {
            throw new BizException("没有选择任何数据，请重新操作！");
        }

        asList.forEach(value -> {
            Map<String, Object> placeInfoIdMap = new HashMap<>(10);
            placeInfoIdMap.put("place_info_id", value);
            placeInfoAttrValueService.removeByMap(placeInfoIdMap);
        });

        return this.removeByIds(asList);
    }

    @Override
    public CmsPlaceInfoVo detail(Long id) {

        CmsPlaceInfo cmsPlaceInfo = this.getById(id);
        CmsPlaceInfoVo cmsPlaceInfoVo = conversionToVo(cmsPlaceInfo);

        //获取附加属性
        List<CmsPlaceInfoAttrValueVo> valueVoList = getAttrValue(id);
        cmsPlaceInfoVo.setAttrValueVoList(valueVoList);

        return cmsPlaceInfoVo;
    }

    @Override
    public Integer countById(Long id) {

        QueryWrapper<CmsPlaceInfo> condition = new QueryWrapper<CmsPlaceInfo>()
                .eq("id", id).last("limit 1");
        Integer count = placeInfoMapper.selectCount(condition);

        log.info("id = {} ,count = {}", id, count);
        return count;
    }


    /**
     * ===========  私有方法 ===========
     */
    private CmsPlaceInfoVo conversionToVo(CmsPlaceInfo cmsPlaceInfo) {
        if (ObjectUtils.isEmpty(cmsPlaceInfo)) {
            return new CmsPlaceInfoVo();
        }

        CmsPlaceInfoVo cmsPlaceInfoVo = new CmsPlaceInfoVo();
        BeanUtil.copyProperties(cmsPlaceInfo, cmsPlaceInfoVo);

        //添加分类中文名
        Long placeTypeId = cmsPlaceInfo.getPlaceTypeId();
//        CmsPlaceTypeVo placeTypeVo = placeTypeService.detail(placeTypeId);
        CmsPlaceType placeTypeVo = placeTypeMapper.selectById(placeTypeId);
        log.info("placeTypeVo = {}", placeTypeVo);
        cmsPlaceInfoVo.setPlaceTypeName(placeTypeVo.getTypeName());

        return cmsPlaceInfoVo;
    }

    private CmsPlaceInfo conversionToPojo(CmsPlaceInfoForm appsIconForm) {
        if (ObjectUtils.isEmpty(appsIconForm)) {
            throw new BizException("提交的数据表单为空");
        }

        CmsPlaceInfo cmsPlaceInfo = new CmsPlaceInfo();
        BeanUtil.copyProperties(appsIconForm, cmsPlaceInfo);

        return cmsPlaceInfo;
    }

    private List<CmsPlaceInfoAttrValueVo> conversionToAttrVoList(List<CmsPlaceInfoAttrValue> list) {

        return list.stream().map(this::conversionToAttrVo).collect(Collectors.toList());
    }

    private CmsPlaceInfoAttrValueVo conversionToAttrVo(CmsPlaceInfoAttrValue attrValue) {
        CmsPlaceInfoAttrValueVo vo = new CmsPlaceInfoAttrValueVo();
        BeanUtil.copyProperties(attrValue, vo);
        return vo;
    }

    /**
     * 判断此评论是否是自己的评论
     *
     * @param commentId 地图点ID
     * @param userId    用户ID
     * @return 是否是自己的评论
     */
    private boolean isComment(Long commentId, Long userId) {
        Integer count = placeInfoCommentService.selectCount(commentId, userId);
        if (count > 0) {
            return true;
        } else {
            return false;
        }
    }

    private CmsPlaceInfoCommentVo conversionToCommentVo(CmsPlaceInfoComment pojo) {
        CmsPlaceInfoCommentVo vo = new CmsPlaceInfoCommentVo();
        BeanUtil.copyProperties(pojo, vo);
        // 添加格式化时间
        vo.setGmtCreateDate(pojo.getGmtCreate());

        return vo;
    }

    @Override
    public List<CmsPlaceInfoAttrValueVo> getAttrValue(Long placeInfoId) {
        log.info("placeInfoId = {}", placeInfoId);
        LambdaQueryWrapper<CmsPlaceInfoAttrValue> queryWrapper = new LambdaQueryWrapper<CmsPlaceInfoAttrValue>()
                .eq(CmsPlaceInfoAttrValue::getPlaceInfoId, placeInfoId);
        List<CmsPlaceInfoAttrValue> attrValueList = placeInfoAttrValueService.list(queryWrapper);

        if (CollectionUtils.isNotEmpty(attrValueList)) {
            List<CmsPlaceInfoAttrValueVo> valueVoList = conversionToAttrVoList(attrValueList);
            return valueVoList;
        }
        return null;
    }



}
